fastlane_require 'dotenv'
fastlane_require 'jwt'
fastlane_require 'base64'
fastlane_require 'net/sftp'
fastlane_require 'json'

default_platform(:android)

build = number_of_commits + 1958 # adding 1958 for legacy reasons. Must be in sync with getVersionCode() from build.gradle
version = get_version_name(
                gradle_file_path:"build.gradle",
                ext_constant_name:"androidVersionName")
version = version.delete "'"

playstoreMetadataDir = "../src/playstore/fastlane/metadata/android/"
fdroidMetadataDir = "../src/fdroid/fastlane/metadata/android/"
liteMetadataDir = "../src/lite/fastlane/metadata/android/"
changeLogSubPath = "changelogs/default.txt"

releaseNotePathEn = "release-notes-en.txt"
releaseNotePathDe = "release-notes-de.txt"

platform :android do |options|

  desc "Run all the tests"
  lane :test do |options|
    gradle(task: "test")
  end

  desc "Deploy new version to Google Play and APK Store options: beta:false (default)"
  lane :deploy do |options|
    checkMetadata(alpha:options[:alpha], beta:options[:beta])

    checkVersionCodeSet(alpha:options[:alpha], beta:options[:beta])

    deployToPlaystore(alpha:options[:alpha], beta:options[:beta])
    deployToServer(alpha:options[:alpha], beta:options[:beta])
    deployToFDroid(alpha:options[:alpha], beta:options[:beta])
    deployLite(alpha:options[:alpha], beta:options[:beta])
    createGitHubDraftRelease(alpha:options[:alpha], beta:options[:beta])

    slack(
      default_payloads: [], # reduce the notification to the minimum
      message: ":rocket: Successfully deployed #{version} with code #{build} :cryptomator:",
      payload: {
        "Changes" => File.read(releaseNotePathEn)
      }
    )
  end

  desc "Check Metadata"
  lane :checkMetadata do |options|
    puts "Make sure you run and commited updateMetadata before tagging and releasing!!!"
    puts "If check failed, run updateMetadata, commit/tag it and restart when finished"

    metadataDirs = [playstoreMetadataDir, fdroidMetadataDir, liteMetadataDir]
    for metadataDir in metadataDirs do
      sh("diff -s #{releaseNotePathDe} #{metadataDir}de-DE/#{changeLogSubPath} > /dev/null")
      sh("diff -s #{releaseNotePathEn} #{metadataDir}en-US/#{changeLogSubPath} > /dev/null")
      sh("diff -s #{releaseNotePathEn} #{metadataDir}fr-FR/#{changeLogSubPath} > /dev/null")
    end
  end

  desc "Update Metadata"
  lane :updateMetadata do |options|
    metadataDirs = [playstoreMetadataDir, fdroidMetadataDir, liteMetadataDir]
    for metadataDir in metadataDirs do
      FileUtils.cp(releaseNotePathDe, "#{metadataDir}de-DE/#{changeLogSubPath}")
      FileUtils.cp(releaseNotePathEn, "#{metadataDir}en-US/#{changeLogSubPath}")
      FileUtils.cp(releaseNotePathEn, "#{metadataDir}fr-FR/#{changeLogSubPath}")
    end
  end

  desc "Check if the version code was set"
  lane :checkVersionCodeSet do |options|
    puts "Make sure you adjusted androidVersionName and set androidVersionCode to #{build} before building and tagging!!!"
    puts "If check failed, adjust/commit/tag it and restart when finished"
    puts "When merged to develop, set it back to getVersionCode()"

    sh("cat ../build.gradle | grep -q 'androidVersionCode = #{build}'")
  end

  desc "Deploy new version to Play Store"
  private_lane :deployToPlaystore do |options|
    deploy_target = "production"

    if options[:alpha]
      deploy_target = "alpha"
    elsif options[:beta]
       deploy_target = "beta"
    end

    gradle(task: "clean")

    gradle(
      task: "assemble",
      build_type: "Release",
      flavor: "playstore",
      print_command: false,
      properties: {
        "android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
        "android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
        "android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
        "android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
      }
    )

    checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'playstore')
    checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'playstore')

    upload_to_play_store(
      track: deploy_target,
      apk: lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH],
      mapping: lane_context[SharedValues::GRADLE_MAPPING_TXT_OUTPUT_PATH],
      version_name: version,
      version_code: build,
      release_status: "draft",
      json_key: ENV["GOOGLE_PLAYSTORE_PRIVATE_KEY_FILE_PATH"],
      skip_upload_aab: true,
      skip_upload_metadata: false,
      skip_upload_images: true,
      skip_upload_screenshots: true,
      metadata_path: "src/playstore/fastlane/metadata/android/"
    )

    FileUtils.cp(lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], "release/Cryptomator-#{version}_playstore_signed.apk")
  end

  desc "Deploy new version to server"
  private_lane :deployToServer do |options|
    gradle(task: "clean")

    gradle(
      task: "assemble",
      build_type: "Release",
      flavor: "apkstore",
      print_command: false,
      properties: {
        "android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
        "android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
        "android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
        "android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
      }
    )

    checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'apkstore')
    checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'apkstore')

    FileUtils.cp(lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], "release/Cryptomator-#{version}.apk")

    server_host = ENV["APK_STORE_BASIC_URL"]
    base_url = "https://#{server_host}/android/"
    apk_url = "#{base_url}#{version}/Cryptomator-#{version}.apk"
    apk_sha_256 = Digest::SHA256.hexdigest File.read "release/Cryptomator-#{version}.apk"
    release_note_url = "#{base_url}#{version}/release-notes.html"

    claims = {
      "version": version,
      "url": apk_url,
      "apk_sha_256": apk_sha_256,
      "release_notes": release_note_url
    }

    private_key = OpenSSL::PKey.read(File.read(ENV["SIGNING_UPDATE_APK_STORE_KEY_PATH"])) 
    token = JWT.encode claims, private_key, "ES256"

    latest_version_filename = "latest-version-#{version}.json"

    latest_version_jsn = File.new("latest_versions/#{latest_version_filename}","w")
    latest_version_jsn.write(token)
    latest_version_jsn.close

    if options[:alpha] or options[:beta]
      puts "Skipping deployment to server cause there isn't currently a alpha/beta channel"
    else
      puts "Uploading APK and release note"

      aws_s3(
        bucket: ENV['S3_BUCKET'],
        endpoint: ENV['S3_ENDPOINT'],
        region: ENV['S3_REGION'],
        access_key: ENV['S3_ACCESS_KEY'],
        secret_access_key: ENV['S3_SECRET_ACCESS_KEY'],
        path: "android/#{version}",
        files: [
          "fastlane/release/Cryptomator-#{version}.apk",
          "fastlane/release-notes.html"
        ],
        skip_html_upload: true,
        apk: ''
      )
  
      puts "Uploading #{latest_version_filename} with claims #{claims}"
      puts "Rename #{latest_version_filename} to latest-version.json for deployment"
  
      aws_s3(
        bucket: ENV['S3_BUCKET'],
        endpoint: ENV['S3_ENDPOINT'],
        region: ENV['S3_REGION'],
        access_key: ENV['S3_ACCESS_KEY'],
        secret_access_key: ENV['S3_SECRET_ACCESS_KEY'],
        path: "android",
        files: [
          "fastlane/latest_versions/#{latest_version_filename}"
        ],
        skip_html_upload: true,
        apk: ''
      )
    end

    FileUtils.mv("release/Cryptomator-#{version}.apk", "release/Cryptomator-#{version}_signed.apk")
  end

  desc "Deploy new version to F-Droid"
  private_lane :deployToFDroid do |options|
    gradle(task: "clean")

    gradle(
      task: "assemble",
      build_type: "Release",
      flavor: "fdroid",
      print_command: false,
      properties: {
        "android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
        "android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
        "android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
        "android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
      }
    )

    checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'fdroid')
    checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'fdroid')

    if options[:alpha] or options[:beta]
      puts "Skipping deployment to F-Droid cause there isn't currently a alpha/beta channel"
    else
      puts "Updating F-Droid"

      FileUtils.cp(lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], "repo/Cryptomator.apk")

      sh("cp -r #{fdroidMetadataDir} metadata/org.cryptomator/")
      FileUtils.cp("metadata/org.cryptomator/en-US/changelogs/default.txt", "metadata/org.cryptomator/en-US/changelogs/#{version}.txt")
      FileUtils.cp("metadata/org.cryptomator/de-DE/changelogs/default.txt", "metadata/org.cryptomator/de-DE/changelogs/#{version}.txt")
      sh("fdroid update && fdroid rewritemeta")
      sh("rm -r metadata/org.cryptomator/")

      aws_s3(
        bucket: ENV['S3_BUCKET'],
        endpoint: ENV['S3_ENDPOINT'],
        region: ENV['S3_REGION'],
        access_key: ENV['S3_ACCESS_KEY'],
        secret_access_key: ENV['S3_SECRET_ACCESS_KEY'],
        path: "android/fdroid",
        folder: "fastlane/repo",
        skip_html_upload: true,
        apk: ''
      )
    end

    FileUtils.cp(lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], "release/Cryptomator-#{version}_fdroid_signed.apk")
  end

  desc "Deploy new lite version"
  private_lane :deployLite do |options|
    sh("docker build -t cryptomator-android ../buildsystem")
    sh("docker run --rm -u $(id -u):$(id -g) -v $(cd .. && pwd):/project -w /project cryptomator-android ./gradlew clean assembleLiteRelease")

    sh("zipalign -v -p 4 ../presentation/build/outputs/apk/lite/release/presentation-lite-release-unsigned.apk presentation-lite-release-unsigned-aligned.apk")
    sh("apksigner sign --ks #{ENV["SIGNING_KEYSTORE_PATH"]} --ks-key-alias #{ENV["SIGNING_KEY_ALIAS"]} --ks-pass env:SIGNING_KEYSTORE_PASSWORD --key-pass env:SIGNING_KEY_PASSWORD --out release/Cryptomator-#{version}_lite_signed.apk presentation-lite-release-unsigned-aligned.apk")
	  sh("rm presentation-lite-release-unsigned-aligned.apk")

    lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] = File.join(Dir.pwd, 'release', "Cryptomator-#{version}_lite_signed.apk")

    checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'lite')
    checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'lite')
  end

  desc "Check if tracking added in some dependency using Izzy's script"
  lane :checkTrackingAddedInDependencyUsingIzzyScript do |options|
    flavor = options[:flavor]

    puts "Check if script file is latest"
    Dir.chdir("izzyscript") do
      sh("wget -O current_iod-scan-apk.php https://gitlab.com/fdroid/issuebot/-/raw/master/modules/iod-scan-apk.php")

      same_script = FileUtils.compare_file("iod-scan-apk.php", "current_iod-scan-apk.php")
      if same_script
        puts "Script file unchanged"
        FileUtils.rm("current_iod-scan-apk.php")
      else
        UI.error("Script updated, check diff, download and save to iod-scan-apk.php")
        fail
      end

      FileUtils.mkdir("unsigned")

      FileUtils.cp(lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], "unsigned/org.cryptomator_fdroid.apk")

      sh("ISSUEBOT_CURRENT_APPLICATION_ID=org.cryptomator ISSUEBOT_CURRENT_REPLY_FILE=current_result_#{flavor}.json php iod-scan-apk.php")

      # clean up
      FileUtils.rm("libinfo.jsonl")
      FileUtils.rm("libsmali.jsonl")
      FileUtils.rm("org.cryptomator_fdroid.apk")
      FileUtils.rm_r("unsigned")
      FileUtils.rm_r("org.cryptomator_fdroid")

      puts "Check if something changed in the APK regarding the dependencies"

      report = JSON.parse(File.read("result_#{flavor}.json"))["report"]
      current_report = JSON.parse(File.read("current_result_#{flavor}.json"))["report"]

      if report.eql?(current_report)
        puts "Dependencies unchanged"
        FileUtils.rm("current_result_#{flavor}.json")
      else
        UI.error("Dependencies changed, check result of current_result.json, if no problem, move it to result.json, commit and retry")
        fail
      end
    end
  end

  desc "Check if tracking added in some dependency using exodus"
  lane :checkTrackingAddedInDependencyUsingExodus do |options|
    FileUtils.mkdir("exodus-test")

    FileUtils.cp(lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH], "exodus-test/org.cryptomator.apk")

    puts "Check if a tracking library was added"
    sh("docker run -v $(pwd)/exodus-test/org.cryptomator.apk:/app.apk --rm -i exodusprivacy/exodus-standalone | tail -1 | grep -q 'Found trackers: 0'")

    FileUtils.rm_r("exodus-test")
  end

  desc "Create GitHub draft release"
  lane :createGitHubDraftRelease do |options|
    target_branch = "main"
    prerelease = false

    if options[:alpha] or options[:beta]
      target_branch = git_branch
      prerelease = true
    end

    website_apk_sha256 = Digest::SHA256.hexdigest File.read "release/Cryptomator-#{version}_signed.apk"
    fdroid_apk_sha256 = Digest::SHA256.hexdigest File.read "release/Cryptomator-#{version}_fdroid_signed.apk"
    lite_sha256 = Digest::SHA256.hexdigest File.read "release/Cryptomator-#{version}_lite_signed.apk"

    release_note = "## What's New\n\n" + File.read(releaseNotePathEn) + "\n\n---\n\nSHA256 Signature: `#{website_apk_sha256}`\nSHA256 Signature fdroid: `#{fdroid_apk_sha256}`\nSHA256 Signature lite: `#{lite_sha256}`\n"

    puts release_note

    github_release = set_github_release(
      repository_name: "cryptomator/android",
      api_token: ENV["GITHUB_PERSONAL_ACCESS_TOKEN"],
      name: "#{version}",
      tag_name: "#{version}",
      description: release_note,
      commitish: target_branch,
      is_draft: true,
      is_prerelease: prerelease,
      upload_assets: ["fastlane/release/Cryptomator-#{version}_fdroid_signed.apk", "fastlane/release/Cryptomator-#{version}_lite_signed.apk", "fastlane/release/Cryptomator-#{version}_signed.apk"]
    )
  end

  desc "Dry run - check tracking added for all flavors"
  lane :dryRun do |options|
    checkMetadata(alpha:options[:alpha], beta:options[:beta])

    checkVersionCodeSet(alpha:options[:alpha], beta:options[:beta])

    gradle(task: "clean")

    gradle(
      task: "assemble",
      build_type: "Release",
      flavor: "playstore",
      print_command: false,
      properties: {
        "android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
        "android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
        "android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
        "android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
      }
    )

    checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'playstore')
    checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'playstore')

    gradle(task: "clean")

    gradle(
      task: "assemble",
      build_type: "Release",
      flavor: "apkstore",
      print_command: false,
      properties: {
        "android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
        "android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
        "android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
        "android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
      }
    )

    checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'apkstore')
    checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'apkstore')

    gradle(task: "clean")

    gradle(
      task: "assemble",
      build_type: "Release",
      flavor: "fdroid",
      print_command: false,
      properties: {
        "android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
        "android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
        "android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
        "android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
      }
    )

    checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'fdroid')
    checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'fdroid')

    gradle(task: "clean")

    gradle(
      task: "assemble",
      build_type: "Release",
      flavor: "lite",
      print_command: false,
      properties: {
        "android.injected.signing.store.file" => ENV["SIGNING_KEYSTORE_PATH"],
        "android.injected.signing.store.password" => ENV["SIGNING_KEYSTORE_PASSWORD"],
        "android.injected.signing.key.alias" => ENV["SIGNING_KEY_ALIAS"],
        "android.injected.signing.key.password" => ENV["SIGNING_KEY_PASSWORD"],
      }
    )

    checkTrackingAddedInDependencyUsingIzzyScript(alpha:options[:alpha], beta:options[:beta], flavor: 'lite')
    checkTrackingAddedInDependencyUsingExodus(alpha:options[:alpha], beta:options[:beta], flavor: 'lite')
  end
end
